package JET.graphics.jogl;

import JET.Constants;
import JET.Centers.ControlCenter;
import JET.Centers.Graphics.GraphicMode;
import JET.Scene;
import JET.geom.Vec2d;
import JET.graphics.GraphicPanel;
import JET.mygui.CenterTabsPane.CenterTabsPaneTab;
import JET.mygui.CenterTabsPane.TabType;
import JET.physics.BoundingArea;
import JET.physics.RSpring;
import JET.physics.Spring;
import JET.physics.bodies.Body;
import JET.physics.bodies.SpringBody;
import JET.physics.engine.JETContext;
import java.awt.geom.Rectangle2D;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

/**
 *
 * @author raos
 */
public class JoglGraphicPanel extends GLCanvas implements GraphicPanel, /*CenterTabsPaneTab,*/ Constants, GLEventListener {

    static final ControlCenter CC = ControlCenter.getInstance();

    protected float mainGriddX = 50;
    protected float mainGriddY = 50;

    protected float helpGriddX = 5;
    protected float helpGriddY = 5;

    protected double viewScale = 1.;

    /** Position of the view */
    public float viewX = 0.f,
                 viewY = 0.f;

    private Scene scene;
            
    public JoglGraphicPanel(GLCapabilities glCapailities, String sceneName) {
        //super(glCapailities);
        this.scene = CC.getScene(sceneName);
//        this.addGLEventListener(new GraphicPanelGLRenderer(this));
        this.addGLEventListener(this);
    }

    /**
     * Called from within initComponents().
     * hint: to customize the generated code choose 'Customize Code' in the contextmenu
     * of the selected UI Component you wish to cutomize in design mode.
     * @return Returns customized GLCapabilities.
     */
    public static GLCapabilities createGLCapabilites() {

        GLCapabilities capabilities = new GLCapabilities();
        capabilities.setHardwareAccelerated(true);

        // try to enable 2x anti aliasing - should be supported on most hardware
        //capabilities.setNumSamples(2);
        //capabilities.setSampleBuffers(true);

        return capabilities;
    }

    public void init(GLAutoDrawable drawable) {
        // Use debug pipeline
        // drawable.setGL(new DebugGL(drawable.getGL()));

        GL gl = drawable.getGL();
        System.err.println("INIT GL IS: " + gl.getClass().getName());

        // Enable VSync
        //gl.setSwapInterval(1);

        // Setup the drawing area and shading mode
        gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        gl.glShadeModel(GL.GL_FLAT); // try setting this to GL_FLAT and see what happens.
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        System.out.println("x: "+x+" y: "+y+" w: "+width+" height: "+height);
        GL gl = drawable.getGL();
        GLU glu = new GLU();

        if (height <= 0) { // avoid a divide by zero error!

            height = 1;
        }
        final float h = (float) width / (float) height;
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        //glu.gluPerspective(120.0f, h, 1.0, 20.0);
        //glu.gluPerspective(y, height, h, h)
        glu.gluOrtho2D(0, width, 0, height);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();

        // Clear the drawing area
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        gl.glLoadIdentity();
        gl.glTranslatef(viewX, viewY, 0);
        gl.glScaled(viewScale, viewScale, 0.);

        // Draw scene springs
        gl.glBegin(gl.GL_LINES);
        if (scene.GC.isDrawSprings2d()) {
            GLHelper.setColor(gl, ENGINE_SPRING_COLOR);
            for(Spring s : scene.getJETContext().getSpringList())
                if (s.isAlive()) {
                    gl.glVertex2d(s.getM1().getPosition().getX(), s.getM1().getPosition().getY());
                    gl.glVertex2d(s.getM2().getPosition().getX(), s.getM2().getPosition().getY());
                }
        }
        gl.glEnd();

        // Draw scene rotary springs
        if (scene.GC.isDrawSprings2d()) {
            for(RSpring rs : scene.getJETContext().getRSpringList()) {
                GLHelper.setColor(gl, ENGINE_RSPRING_COLOR);
                gl.glLoadIdentity();
                gl.glTranslatef(viewX, viewY, 0);
                gl.glScaled(viewScale, viewScale, 0.);
                
                gl.glBegin(gl.GL_LINE_STRIP);
                    gl.glVertex2d(rs.getM1().getPosition().getX(), rs.getM1().getPosition().getY());
                    gl.glVertex2d(rs.getM2().getPosition().getX(), rs.getM2().getPosition().getY());
                    gl.glVertex2d(rs.getM3().getPosition().getX(), rs.getM3().getPosition().getY());
                gl.glEnd();

                gl.glLoadIdentity();
                gl.glTranslatef(viewX, viewY, 0);
                gl.glScaled(viewScale, viewScale, 0.);
                gl.glTranslated(rs.getM2().getPosition().getX(), rs.getM2().getPosition().getY(), 0);
                
                GLHelper.setColor(gl, ENGINE_RSPRING_ARC_COLOR);
                GLHelper.drawCircle(gl, RSPRING_ARC_RADIUS);
            }
        }

        // Draw scene spring bodies
        for(SpringBody sb : scene.getJETContext().getSpringBodyList())
            GLHelper.drawSpringBody(gl, this, sb);

        // Flush all drawing operations to the graphics card
        gl.glFlush();
    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }

////////////////////////////////////////////////////////////////////////////////
//                       G R A P H I C   P A N E L                            //
////////////////////////////////////////////////////////////////////////////////

    public double fixVertical(double y) {
        return y*-1 + this.getHeight()-1; //engine.getMainBound().getHeight();
    }
    public double getViewScale() {
        return viewScale;
    }
    public double getTranslateX() {
        return this.viewX;
    }
    public double getTranslateY() {
        return this.viewY;
    }

    public void setViewScale(double viewScale) {
        this.viewScale = viewScale;
    }

    public void resizeToFitViewScale() {
    }

    public double engineToScrX(double x) {
        return x * viewScale + viewX;
    }
    public double engineToScrY(double y) {
        return fixVertical(y*viewScale) - viewY;
    }
    public Vec2d engineToScrVec(Vec2d vec) {
        return new Vec2d( engineToScrX(vec.getX()), engineToScrY(vec.getY()));
    }
    
    public double scrToEngineX(double x) {
        return  (x-viewX)/viewScale;
    }
    public double scrToEngineY(double y) {
        return fixVertical(y+viewY)/viewScale ;
    }
    public Vec2d scrToEngineVec(Vec2d vec) {
        return new Vec2d( scrToEngineX(vec.getX()), scrToEngineY(vec.getY()));
    }

    public double monitorToScrX(double mx) {
        return -(this.getLocationOnScreen().getX() - mx);
    }
    public double monitorToScrY(double my) {
        return -(this.getLocationOnScreen().getY() - my);
    }
    public double monitorToEngineX(double mx) {
        return scrToEngineX( monitorToScrX(mx) );
    }
    public double monitorToEngineY(double my) {
        return scrToEngineY( monitorToScrY(my) );
    }
    
    @Override
    public GraphicMode getGraphicMode() {
        return GraphicMode.OpenGL;
    }

    @Override
    public void centerView() {
        
    }

    @Override
    public void centerViewToVec(Vec2d vec) {
        double nvx = getWidth()/2. -  engineToScrX(vec.getX());
        double nvy = -getHeight()/2. + engineToScrY(vec.getY());
        viewX += (float)nvx;
        viewY += (float)nvy;
    }

    @Override
    public double getHelpGriddX() {
        return helpGriddX;
    }

    @Override
    public double getHelpGriddY() {
        return helpGriddY;
    }

    @Override
    public double getMainGriddX() {
        return mainGriddX;
    }

    @Override
    public double getMainGriddY() {
        return mainGriddY;
    }

    //TODO: move getActionFrameSideBoxRect(...) into some static class (i.ex. GraphicHelper)
    @Override
    public Rectangle2D getActionFrameSideBoxRect(Body body, int index)
    {
        BoundingArea ba = body.getBoundingArea();

        double space = 2.5 * viewScale;

        double x1 = engineToScrX( ba.getMinX() ) -space;
        double y1 = engineToScrY( ba.getMaxY() ) -space;
        double x2 = engineToScrX( ba.getMaxX() ) +space;
        double y2 = engineToScrY( ba.getMinY() ) +space;

        double width = x2-x1;
        double height = y2-y1;
        double scaleBoxSize = ACTION_FRAME_SIDE_BOX_SIZE;

        if (index == 0) // Action Frame Rect
        {
            return new Rectangle2D.Double(x1, y1, x2-x1, y2-y1);
        }
        else if (index == 1)  // Top Left Rect
        {
            double x = x1 - scaleBoxSize;
            double y = y1 - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 2) // Middle Top Rect
        {
            double x = x1 + width/2. - scaleBoxSize;
            double y = y1 - scaleBoxSize;

            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 3) // Top Right
        {
            double x = x2 - scaleBoxSize;
            double y = y1 - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 4) // Right Middle
        {
            double x = x2 - scaleBoxSize;
            double y = y1 + height/2. - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 5) // Bottom Right
        {
            double x = x2 - scaleBoxSize;
            double y = y2 - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 6) // Bottom Middle
        {
            double x = x1 + width/2. - scaleBoxSize;
            double y = y2 - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 7) // Bottom Left
        {
            double x = x1 - scaleBoxSize;
            double y = y2 - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }
        else if (index == 8) // Left Middle
        {
            double x = x1 - scaleBoxSize;
            double y = y1 + height/2. - scaleBoxSize;
            return new Rectangle2D.Double(x,y, scaleBoxSize*2, scaleBoxSize*2);
        }

        return null;
    }

    /*
    public TabType getTabType() {
        return TabType.GraphicPanel;
    }

    public String getTitle() {
        return "Scene [OpenGL]";
    }
    */

    public Scene getScene() {
        return scene;
    }

    public String getSceneName() {
        return scene.getName();
    }

//    public Cursor getCursor(Cursor cursor) {
//        throw new UnsupportedOperationException("Not supported yet.");
//    }

//    public void paintComponent() {
//        throw new UnsupportedOperationException("Not supported yet.");
//    }

}
